﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Bot.Domain.DialogFlow
{
    public interface IHttpWebRequestMethod
    {
        Task<HttpResponseMessage> GetAsync(HttpRequestMessage request, string key, int timeout=60);
        Task<HttpResponseMessage> PostAsync(HttpRequestMessage request, string key, int timeout = 60);
        //Delete();
        //Put();
        //Patch();
    }
    public class HttpWebRequestMethod : IHttpWebRequestMethod
    {
        private bool Debug { get; } = false;
        public HttpWebRequestMethod()
        {
#if DEBUG
            Debug = true;
#endif
        }

        public ITokenService tokenService { get; set; }

        public async Task<HttpResponseMessage> GetAsync(HttpRequestMessage request, string key, int timeout = 60)
        {
            HttpResponseMessage httpResponseMessage = null;
            request.Method = HttpMethod.Get;
           
            httpResponseMessage = await SendAsync(request, key, timeout);           
            return httpResponseMessage;
        }
        public async Task<HttpResponseMessage> PostAsync(HttpRequestMessage request, string key, int timeout = 60)
        {
            HttpResponseMessage httpResponseMessage = null;
            request.Method = HttpMethod.Post;            
            httpResponseMessage = await SendAsync(request, key, timeout);            
            return httpResponseMessage;
        }
    
        private async Task<string> GetToken(string key, bool restore)
        {
            //LogHelper.Debug(JsonConvert.SerializeObject(key));

            string accessToken = "";
            if (restore)
            {
                //不管什么时候都重新获取 token
                if (key.Contains("GoogleCredJson_"))
                {
                    string[] keyArr = key.Split('_');
                    int siteId = int.Parse(keyArr[1]);
                    Guid botId = Guid.Parse(keyArr[2]);

                    accessToken = await tokenService.GetToken(siteId, botId);
                    GoogleCredentialDic.tokenDic[key] = accessToken;
                }
                else
                {
                    accessToken = await tokenService.GetToken(key);
                }
            }
            else
            {
                //从内存中取，如果为空，重新获取
                GoogleCredentialDic.tokenDic.TryGetValue(key, out accessToken);
                if (string.IsNullOrEmpty(accessToken))
                {
                    if (key.Contains("GoogleCredJson_"))
                    {
                        string[] keyArr = key.Split('_');
                        int siteId = int.Parse(keyArr[1]);
                        Guid botId = Guid.Parse(keyArr[2]);

                        accessToken = await tokenService.GetToken(siteId, botId);
                        GoogleCredentialDic.tokenDic[key] = accessToken;
                    }
                    else
                    {
                        accessToken = await tokenService.GetToken(key);
                    }
                }
            }            
    
            //LogHelper.Debug(JsonConvert.SerializeObject(accessToken));
            return accessToken;
        }
        public async Task<HttpResponseMessage> DeleteAsync(HttpRequestMessage request, string key, int timeout = 60)
        {
            HttpResponseMessage httpResponseMessage = null;
            request.Method = HttpMethod.Delete;
            httpResponseMessage = await SendAsync(request, key, timeout);           
            return httpResponseMessage;
        }
        private async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, string key, int timeout = 60)
        {
            HttpResponseMessage httpResponseMessage = null;
            string accessToken = await GetToken(key,false);
            using (var client = new HttpClient())
            {
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
                client.Timeout = TimeSpan.FromSeconds(timeout);
                HttpRequestMessage httpRequestMessageClone = Clone(request); //前移，修复 System.Net.Http.StringContent Cannot access a disposed object
                httpResponseMessage = await client.SendAsync(request);
                if (httpResponseMessage.StatusCode == HttpStatusCode.Unauthorized)
                {
                    accessToken = await GetToken(key,true);            
                    httpRequestMessageClone.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
                    httpResponseMessage = await client.SendAsync(httpRequestMessageClone);
                }
                return httpResponseMessage;
            }
        }
        // using System.Reflection;
        // using System.Net.Http;
        // private const string SEND_STATUS_FIELD_NAME = "_sendStatus";
        private void ResetSendStatus(HttpRequestMessage request)
        {
            TypeInfo requestType = request.GetType().GetTypeInfo();
            FieldInfo sendStatusField = requestType.GetField("_sendStatus", BindingFlags.Instance | BindingFlags.NonPublic);
            if (sendStatusField != null)
                sendStatusField.SetValue(request, 0);
            else
                throw new Exception("Failed to hack HttpRequestMessage, _sendStatus doesn't exist.");
        }
        private HttpRequestMessage Clone(HttpRequestMessage request)
        {
            HttpRequestMessage clone = new HttpRequestMessage(request.Method, request.RequestUri);
            if (request.Content != null)
            {
                string json = request.Content.ReadAsStringAsync().Result;
                clone.Content = new StringContent(json, Encoding.UTF8, "application/json");
            }

            clone.Version = request.Version;
            foreach (KeyValuePair<string, object> prop in request.Properties)
            {
                clone.Properties.Add(prop);
            }
            foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers)
            {
                clone.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }
            return clone;
        }
    }
}
